package com.yanqu.road.server.gameplayer.module.activity.soulbone;

import com.yanqu.road.dao.impl.activity.lottery.UserActivityLotteryGuaranteeDataDaoImpl;
import com.yanqu.road.dao.impl.activity.soulbone.SoulBoneUserDataDaoImpl;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.lottery.UserActivityLotteryGuaranteeData;
import com.yanqu.road.entity.activity.soulbone.SoulBoneConfig;
import com.yanqu.road.entity.activity.soulbone.SoulBoneUserData;
import com.yanqu.road.entity.activity.soulbone.config.SoulBoneLotteryInfo;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.enums.eActivityType;
import com.yanqu.road.entity.enums.eGoodsType;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.entity.log.soulbone.LogSoulBoneLottery;
import com.yanqu.road.pb.activity.SoulBoneProto;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.player.BagModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.ServerLanguageMgr;
import com.yanqu.road.server.manger.activity.ActivityMgr;
import com.yanqu.road.server.manger.activity.SoulBoneActivityMgr;
import com.yanqu.road.server.manger.config.GoodsMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.ConfigHelper;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigInteger;
import java.util.*;

public class SoulBoneModule extends GeneralModule {

    private SoulBoneUserData userData;

    private UserActivityLotteryGuaranteeData guaranteeData;

    private Random random = new Random();

    public SoulBoneModule(GamePlayer player) {
        super(player);
    }

    @Override
    public boolean loadData() {
        ActivityInfo activityInfo = SoulBoneActivityMgr.getActivityInfo();
        if(ActivityMgr.activityInShowTime(activityInfo)) {
            userData = new SoulBoneUserDataDaoImpl().getSoulBoneUserData(activityInfo.getActivityId(), player.getUserId());
        }
        guaranteeData = new UserActivityLotteryGuaranteeDataDaoImpl().getUserActivityLotteryGuaranteeData(player.getUserId(), eActivityType.SoulBoneLottery.getValue());
        if(guaranteeData == null){
            guaranteeData = new UserActivityLotteryGuaranteeData(player.getUserId(), eActivityType.SoulBoneLottery.getValue(), 0, new HashMap<>());
        }
        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        if(userData != null){
            if(userData.isInsertOption()){
                new SoulBoneUserDataDaoImpl().add(userData);
            } else if (userData.isUpdateOption()) {
                new SoulBoneUserDataDaoImpl().update(userData);
            }
        }
        if(guaranteeData.isInsertOption()){
            new UserActivityLotteryGuaranteeDataDaoImpl().add(guaranteeData);
        }else if(guaranteeData.isUpdateOption()){
            new UserActivityLotteryGuaranteeDataDaoImpl().update(guaranteeData);
        }
        return true;
    }

    @Override
    public void afterLogin() {
        activityReload();
    }

    public void activityReload() {

        if (!SystemOpenMgr.systemOpen(player, eSystemId.SoulBoneLotteryActivity.getValue())) {
            return;
        }

        SoulBoneConfig config = SoulBoneActivityMgr.getConfig();
        if(config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())){
            return;
        }

        syncConfig(config);

        initUserData(config);

        if(!resetOneDay()) {
            syncUserData(config);
        }
    }

    private void syncConfig(SoulBoneConfig config) {
        SoulBoneProto.SoulBoneConfigDataSyncMsg.Builder clientMsg = SoulBoneProto.SoulBoneConfigDataSyncMsg.newBuilder();

        for (SoulBoneLotteryInfo lotteryInfo : config.getLotteryInfoList()) {
            SoulBoneProto.SoulBoneLotteryConfigTemp.Builder builder = SoulBoneProto.SoulBoneLotteryConfigTemp.newBuilder();
            builder.setId(lotteryInfo.getId());
            builder.setReward(lotteryInfo.getReward());
            builder.setWeight(lotteryInfo.getWeight());
            builder.setQuality(lotteryInfo.getQuality());
            builder.setName(ServerLanguageMgr.getContent(lotteryInfo.getName(), player.getLanguage()));
            clientMsg.addLottery(builder);
        }

        player.sendPacket(Protocol.U_SOUL_BONE_SYNC_CONFIG, clientMsg);
    }

    private synchronized void initUserData(SoulBoneConfig config) {
        // 系统未解锁
        if (!SystemOpenMgr.systemOpen(player, eSystemId.SoulBoneLotteryActivity.getValue())) {
            return;
        }

        // 没有活动
        if (!ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }

        ActivityInfo activityInfo = config.getActivityInfo();

        // 已经有数据了
        if (userData != null && userData.getActivityId() == activityInfo.getActivityId()) {
            return;
        }

        SoulBoneUserData tmpUserData = new SoulBoneUserData();
        tmpUserData.setActivityId(activityInfo.getActivityId());
        //历史保底部位
        tmpUserData.setGetItemMap(new HashMap<>(guaranteeData.getGetItemMap()));
        //历史保底次数
        long guarantee = guaranteeData.getDrawCount();
        if(guarantee > 0){
            int drawTimes = (int)(guarantee / 1000);
            int sixDrawTimes = (int)(guarantee % 1000);
            SoulBoneActivityMgr.getLogger().info("玩家{}，魂骨保底初始，drawTimes {}， sixDrawTimes {}", player.getUserId(), drawTimes, sixDrawTimes);
            tmpUserData.setDrawTimes(drawTimes);
            tmpUserData.setSixTimes(sixDrawTimes);
        }
        tmpUserData.setUserId(getUserId());
        tmpUserData.setInsertOption();
        userData = tmpUserData;
    }

    private boolean resetOneDay() {
        return false;
    }

    private void syncUserData(SoulBoneConfig config) {
        if(userData != null){
            SoulBoneProto.SoulBoneUserDataSyncMsg.Builder clientMsg = SoulBoneProto.SoulBoneUserDataSyncMsg.newBuilder();
            SoulBoneProto.SoulBoneUserDataTemp.Builder builder = buildUserBuilder();
            clientMsg.setUserData(builder);
            player.sendPacket(Protocol.U_SOUL_BONE_SYNC_USER, clientMsg);
        }
    }

    public SoulBoneProto.SoulBoneUserDataTemp.Builder buildUserBuilder() {
        SoulBoneProto.SoulBoneUserDataTemp.Builder builder = SoulBoneProto.SoulBoneUserDataTemp.newBuilder();
        builder.setDrawTimes(userData.getDrawTimes());
        builder.setTotalDrawTimes(userData.getTotalDrawTimes());
        builder.setSixDrawTimes(userData.getSixTimes());
        builder.setJuBao(userData.getJuBao());
        return builder;
    }

    public int draw(int type) {
        int times = 1;
        if(type == 2){
            times = 10;
        }
        if(userData == null){
            return GameErrorCode.E_SYSTEM_NO_OPEN;
        }

        SoulBoneConfig config = SoulBoneActivityMgr.getConfig();

        long bagItemCount = player.getModule(BagModule.class).getUserBagItemCount(config.getItemId());
        times = Math.min(times, (int)(bagItemCount / config.getConsumeNum()));
        if(times <= 0){
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        Property consume = new Property();
        consume.addProperty(config.getItemId(), BigInteger.valueOf(config.getConsumeNum()));
        consume.rideProperty(times);

        if(!player.getModule(CurrencyModule.class).currencyIsEnough(consume)){
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        //六七星
        Map<Integer, List<GoodsInfo>> startPoolMap = getGoodsByStar();

        //中间变量
        int drawTimes = userData.getDrawTimes();
        int sixTimes = userData.getSixTimes();
        Map<Integer, Integer> gotItemMap = new HashMap<>(userData.getGetItemMap());

        Property allProperty = new Property();

        List<String> clientReward = new ArrayList<>();
        boolean containSixStart = false;
        //公告
        List<SoulBoneProto.SoulBoneNoticeMsg> noticeList = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            SoulBoneLotteryInfo soulBoneLotteryInfo;
            //n次六星后的一抽出7星
            if(sixTimes + 1 >= config.getBigGuaranteeTimes() && drawTimes + 1 >= config.getGuaranteeNum()){
                //七星
                int index = RandomHelper.getRandomIndexByWeight(config.getBigPoolWeightList(), random);
                int id = config.getBigPoolItemList().get(index);
                soulBoneLotteryInfo = config.getSoulBoneLotteryInfo(id);

            }else if(drawTimes + 1 >= config.getGuaranteeNum()) {

                //出六、七星
                int index = RandomHelper.getRandomIndexByWeight(config.getSmallPoolWeightList(), random);
                int id = config.getSmallPoolItemList().get(index);
                soulBoneLotteryInfo = config.getSoulBoneLotteryInfo(id);

            } else {
                //按权重随机
                List<Integer> weightList = new ArrayList<>();
                for (SoulBoneLotteryInfo info : config.getLotteryInfoList()) {
                    weightList.add(info.getWeight());
                }
                int idx = RandomHelper.getRandomIndexByWeight(weightList, random);
                soulBoneLotteryInfo = config.getLotteryInfoList().get(idx);
            }

            //星级
            int star = 0;
            String reward;
            if(soulBoneLotteryInfo.getWeightList().isEmpty()){
                reward = soulBoneLotteryInfo.getReward();
            }else {
                int index = RandomHelper.getRandomIndexByWeight(soulBoneLotteryInfo.getWeightList(), random);
                reward = soulBoneLotteryInfo.getRewardList().get(index);
                Property property = PropertyHelper.parseStringToProperty(reward);
                if(property.isNothing() || property.getGoods().size() > 1){
                    return GameErrorCode.E_GAME_ERROR;
                }
                int goodsId = 0;
                for (Map.Entry<Integer, BigInteger> entry : property.getGoods().entrySet()) {
                    goodsId = entry.getKey();
                    break;
                }
                GoodsInfo goodsInfo = GoodsMgr.getGoodsById(goodsId);
                if(goodsInfo == null || goodsInfo.getType() != eGoodsType.VehicleBone.getValue()){
                    return GameErrorCode.E_GAME_ERROR;
                }
                //星级
                star = goodsInfo.getParamList().get(1).intValue();

                //提前出6星，转换成7星
                if(star == 6 && sixTimes + 1 >= config.getBigGuaranteeTimes()){
                    //七星
                    int recoverIndex = RandomHelper.getRandomIndexByWeight(config.getBigPoolWeightList(), random);
                    int id = config.getBigPoolItemList().get(recoverIndex);
                    soulBoneLotteryInfo = config.getSoulBoneLotteryInfo(id);
                    int rewardIndex = RandomHelper.getRandomIndexByWeight(soulBoneLotteryInfo.getWeightList(), random);
                    reward = soulBoneLotteryInfo.getRewardList().get(rewardIndex);

                    property = PropertyHelper.parseStringToProperty(reward);
                    if(property.isNothing() || property.getGoods().size() > 1){
                        return GameErrorCode.E_GAME_ERROR;
                    }
                    for (Map.Entry<Integer, BigInteger> entry : property.getGoods().entrySet()) {
                        goodsId = entry.getKey();
                        break;
                    }
                    goodsInfo = GoodsMgr.getGoodsById(goodsId);
                    if(goodsInfo == null || goodsInfo.getType() != eGoodsType.VehicleBone.getValue()){
                        return GameErrorCode.E_GAME_ERROR;
                    }
                    //星级
                    star = goodsInfo.getParamList().get(1).intValue();
                }

                if(star >= 6) {
                    //总数量
                    int count = getStarNum(gotItemMap, star);
                    //类型数量
                    Set<Integer> typeNum = getStarType(gotItemMap, star);
                    //是否是部位保底了
                    boolean touch = false;
                    if (star == 6 && touchStar6(config, count, typeNum)) {
                        touch = true;
                    } else if (star == 7 && touchStar7(config, count, typeNum)) {
                        touch = true;
                    }
                    if (touch) {
                        List<GoodsInfo> poolList = startPoolMap.get(star);
                        poolList.removeIf(g -> typeNum.contains(g.getGoodsId()));
                        int idx = random.nextInt(poolList.size());
                        goodsInfo = poolList.get(idx);
                        goodsId = goodsInfo.getGoodsId();
                        reward = PropertyHelper.parseGoodsToString(goodsId, BigInteger.ONE);
                    }
                    int old = gotItemMap.getOrDefault(goodsId, 0);
                    gotItemMap.put(goodsId, old + 1);
                    //重置保底
                    Set<Integer> newTypeNum = getStarType(gotItemMap, star);
                    if(newTypeNum.size() == 6){
                        for (Integer id : newTypeNum) {
                            gotItemMap.remove(id);
                        }
                    }
                }
            }

            allProperty.addProperty(PropertyHelper.parseStringToProperty(reward));
            clientReward.add(reward);

            drawTimes++;
            if(star == 6){
                sixTimes++;
                //重置200抽
                drawTimes = 0;
                containSixStart = true;
            }
            if(star == 7){
                //大保底触发，才重置小保底
                drawTimes = 0;
                //重置大保底统计
                sixTimes = 0;
                containSixStart = true;
            }
            if(star >= config.getMarqueeStar()){
                SoulBoneProto.SoulBoneNoticeMsg.Builder noticeBuilder = SoulBoneProto.SoulBoneNoticeMsg.newBuilder();
                noticeBuilder.setNickName(player.getUserInfo().getNickName());
                noticeBuilder.setServerId(ConfigHelper.getLong("serverId"));
                noticeBuilder.setTime(System.currentTimeMillis());
                noticeBuilder.setType(0);
                noticeBuilder.setReward(reward);
                noticeBuilder.setParams("");
                noticeList.add(noticeBuilder.build());
            }
        }

        if(!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.SoulBoneLottery, eLogMoneyType.SoulBoneLotteryDrawConsume)){
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        player.getModule(CurrencyModule.class).addCurrency(allProperty, eLogMoneyType.SoulBoneLottery, eLogMoneyType.SoulBoneLotteryDrawReward);

        userData.setTotalDrawTimes(userData.getTotalDrawTimes() + times);
        userData.setDrawTimes(drawTimes);
        userData.setSixTimes(sixTimes);
        userData.setGetItemMap(gotItemMap);

        //更新当前次数保底
        guaranteeData.setDrawCount(drawTimes * 1000 + sixTimes);
        guaranteeData.setGetItemMap(new HashMap<>(gotItemMap));

        SoulBoneProto.SoulBoneDrawRespMsg.Builder clientMsg = SoulBoneProto.SoulBoneDrawRespMsg.newBuilder();
        clientMsg.setReward(StringUtils.listToString(clientReward, ";"));
        clientMsg.setUserData(buildUserBuilder());
        player.sendPacket(Protocol.U_SOUL_BONE_DRAW, clientMsg.setRet(0));

        if(!noticeList.isEmpty()){
            SoulBoneProto.SoulBoneUploadNoticeMsg.Builder uploadMsg = SoulBoneProto.SoulBoneUploadNoticeMsg.newBuilder();
            uploadMsg.setActivityId(userData.getActivityId());
            uploadMsg.addAllNotice(noticeList);
            player.sendPacket(Protocol.C_SOUL_BONE_NOTICE_UPLOAD, uploadMsg);
        }

        //日志
        List<Map<String, Object>> logObjectMap = new ArrayList<>();
        for (Map.Entry<Integer, BigInteger> entry : allProperty.getGoods().entrySet()) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", entry.getKey());
            map.put("num", entry.getValue());
            logObjectMap.add(map);
        }
        AutoLogMgr.add(new LogSoulBoneLottery(userData.getActivityId(),
                userData.getUserId(),
                times,
                PropertyHelper.parsePropertyToString(consume),
                PropertyHelper.parsePropertyToString(allProperty),
                logObjectMap,
                containSixStart));
        return 0;
    }

    /**
     * 是否触发了6星类型保底
     */
    private boolean touchStar6(SoulBoneConfig config, int count, Set<Integer> typeNum) {
        int guarantee = 0;
        for (int i = 0; i < config.getGuarantee6List().size(); i += 2) {
            if(count >= config.getGuarantee6List().get(i + 1)){
                guarantee = config.getGuarantee6List().get(i);
            }
        }
        return typeNum.size() < guarantee;
    }

    /**
     * 是否触发了7星类型保底
     */
    private boolean touchStar7(SoulBoneConfig config, int count, Set<Integer> typeNum) {
        int guarantee = 0;
        for (int i = 0; i < config.getGuarantee7List().size(); i += 2) {
            if(count >= config.getGuarantee7List().get(i + 1)){
                guarantee = config.getGuarantee7List().get(i);
            }
        }
        return typeNum.size() < guarantee;
    }

    private Map<Integer, List<GoodsInfo>> getGoodsByStar() {
        Map<Integer, List<GoodsInfo>> resultMap = new HashMap<>();
        List<GoodsInfo> list = GoodsMgr.getGoodsListByType(eGoodsType.VehicleBone.getValue());
        for (GoodsInfo goodsInfo : list) {
            //星级
            int start = goodsInfo.getParamList().get(1).intValue();
            if(start >= 6){
                if(!resultMap.containsKey(start)){
                    resultMap.put(start, new ArrayList<>());
                }
                resultMap.get(start).add(goodsInfo);
            }
        }
        return resultMap;
    }

    private int getStarNum(Map<Integer, Integer> gotItemMap, int star) {
        int count = 0;
        for (Map.Entry<Integer, Integer> entry : gotItemMap.entrySet()) {
            int goodsId = entry.getKey();
            GoodsInfo goodsInfo = GoodsMgr.getGoodsById(goodsId);
            if(goodsInfo == null || goodsInfo.getType() != eGoodsType.VehicleBone.getValue()){
                continue;
            }
            //星级
            int param2 = goodsInfo.getParamList().get(1).intValue();
            if(star == param2){
                count += entry.getValue();
            }
        }
        return count;
    }

    private Set<Integer> getStarType(Map<Integer, Integer> gotItemMap, int star) {
        Set<Integer> set = new HashSet<>();
        for (Map.Entry<Integer, Integer> entry : gotItemMap.entrySet()) {
            int goodsId = entry.getKey();
            GoodsInfo goodsInfo = GoodsMgr.getGoodsById(goodsId);
            if(goodsInfo == null || goodsInfo.getType() != eGoodsType.VehicleBone.getValue()){
                continue;
            }
            //星级
            int param2 = goodsInfo.getParamList().get(1).intValue();
            if(star == param2){
                set.add(goodsId);
            }
        }
        return set;
    }

    public int getJuBao() {
        if(userData == null){
            return GameErrorCode.E_SYSTEM_NO_OPEN;
        }

        String juBao = userData.getJuBao();
        if(StringUtils.isNullOrEmpty(juBao)){
            return GameErrorCode.E_SOUL_BONE_JU_BAO_EMPTY;
        }

        Property property = PropertyHelper.parseStringToProperty(juBao);
        userData.setJuBao("");

        player.getModule(CurrencyModule.class).addCurrency(property, eLogMoneyType.SoulBoneLottery, eLogMoneyType.SoulBoneLotteryJuBaoReward);

        SoulBoneProto.SoulBoneGetJuBaoRespMsg.Builder clientMsg = SoulBoneProto.SoulBoneGetJuBaoRespMsg.newBuilder();
        clientMsg.setRet(0);
        clientMsg.setReward(juBao);
        clientMsg.setUserData(buildUserBuilder());
        player.sendPacket(Protocol.U_SOUL_BONE_GET_JUBAO, clientMsg);
        return 0;
    }

    public SoulBoneUserData getUserData() {
        return userData;
    }

    public int getUserDataFromCmd() {
        if(userData == null){
            return GameErrorCode.E_SYSTEM_NO_OPEN;
        }

        SoulBoneProto.SoulBoneGetUserDataRespMsg.Builder clientMsg = SoulBoneProto.SoulBoneGetUserDataRespMsg.newBuilder();
        clientMsg.setRet(0);
        clientMsg.setUserData(buildUserBuilder());
        player.sendPacket(Protocol.U_SOUL_BONE_GET_USER, clientMsg);
        return 0;
    }
}
